<template>
    <div class="common-layout">
        <el-container>
        <el-header class="header" >
            <div class="left">
                <div class="icon">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-white">
                        <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path>
                        <path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
                        <line x1="12" x2="12" y1="19" y2="22"></line>
                    </svg>
                </div>
                <div class="title">
                    <h2>AI角色扮演</h2>
                    <div>与你喜欢的人物对话</div>
                </div>
            </div>
            <div class="right">
                <span>首页</span>
                <el-button clsss="login">登录</el-button>
            </div>

        </el-header>
        <el-main class="main" >
            <div class="search">
                <h2>寻找你想对话的角色</h2>
                <el-input
                v-model="input4"
                class="responsive-input"
                placeholder="搜索角色"
                >
                <template #prefix>
                     <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="absolute left-2 top-1/2 transform -translate-y-1/2 text-gray-400">
                        <circle cx="11" cy="11" r="8"></circle>
                        <line x1="21" x2="16.65" y1="21" y2="16.65"></line>
                    </svg>
                </template>
                </el-input>
                <el-button class="search_btn" @click="filterRole">搜索</el-button>
            </div>
            <div class="box">
                <div class="card_box" v-for="item in roles" :key="item.id">
                    <el-card shadow="hover" class="card">
                        <img
                        :src="item.img"
                        style="width: 100%;height: 170px; "
                        />
                        <div class="card_font">
                            <h2>{{ item.name}}</h2>
                            <div>{{ item.title }}</div>
                        </div>
                        <div class="cardfooter">
                            <div style="margin-bottom: 10px;">{{ item.desc }}</div>
                            <el-tag type="primary" round color="#8642d234" style="color: #8643a3;border:0px">{{ item.tag }}</el-tag>
                            <el-button class="start_chat" @click="openDialog(item)">开始对话</el-button>
                        </div>
                        
                    </el-card>

                    
                </div>
                <!-- 弹框 -->
                <el-dialog @close="closeDialog" v-model="dialogTableVisible" top="7vh"  width="800" class="dialog">
                    <!-- 弹框头部 -->
                    <div class="dialog_header">
                        <img :src='activeRole?.avater' class="dialog_icon">
                        <div class="dialog_title">
                            <h2>{{ activeRole?.name }}</h2>
                            <div style="color: #c0b7b7;">正在等待您的消息...</div>
                        </div>
                    </div>
                    
                    <div class="dialog_main" >
                        <!-- 聊天内容 -->
                        <div class="chat_box" style="max-height: 400px; overflow-y: auto;" ref="chat_box">
                            <div class="chat_message" v-for="chat in chatHistory" :key="chat.id" >
                                <div class="chat_message_user" v-if="chat.role==='user'">
                                    <div class="chat_nickname">您说：</div>
                                    <p class="chat_content">{{ chat.content }}</p>
                                </div>
                                <div class="chat_message_ai" v-if="chat.role==='system'">
                                    <div class="chat_nickname">{{ activeRole?.name }}说：</div>
                                    <p class="chat_content">{{ chat.content }}</p>
                                    <button v-show="chat.is_broadcast" class="broadcast" @click="broadcast(chat.content,activeRole?.per)" >播放</button>
                                </div>
                                
                            </div>
                        </div>
                    </div>
                    <div class="dialog_footer">
                        <div class="dialog_footer_box">
                            <el-input
                            v-model="input3"
                            class="search-input"
                            placeholder="输入您的消息"
                            >
                            <template #suffix>
                                <button @click="send_chat" style="border: 0px;background-color: #fff;">
                                    <svg xmlns="http://www.w3.org/2000/svg" width="20"  height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                        <line x1="22" y1="2" x2="11" y2="13"></line>
                                        <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
                                    </svg>
                                </button>
                            </template>
                            </el-input>
                            <el-button class="search_btn" v-show="is_audio" @click="start_record">
                                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-white">
                                        <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path>
                                        <path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
                                        <line x1="12" x2="12" y1="19" y2="22"></line>
                                    </svg>
                            </el-button>
                            <el-button class="cancle_btn" v-show="!is_audio" @click="end_record">
                                    <div style="font-size: 30px;">×</div>
                            </el-button>
                        </div>
                    </div>
                </el-dialog>
            </div>    
        </el-main>
        </el-container>
    </div>
</template>

<script lang="ts" setup>
import {ref,nextTick} from 'vue'
import Recorder from 'js-audio-recorder';
import axios from 'axios';
// import { nextTick } from 'process';

const input4=ref('')
const input3=ref('')
const is_audio=ref(true)
const dialogTableVisible = ref(false)
const activeRole=ref<Role|null>(null)


//聊天id
let chat_id=ref(0)

//录音
let recorder:any = null

//拿到聊天框
const chat_box=ref<HTMLElement|null>(null)

//角色信息
const roles=ref<Role[]>([])

//聊天记录类型
interface chatHistory{
    id:number,
    role:'user'|'system',
    content:string,
    is_broadcast?:boolean
}
//activeRole类型
interface Role{
    id:number,
    name: string,
    title: string,
    tag: string,
    img: string,
    avater: string,
    desc: string,
    per:number
}

//聊天记录
let chatHistory=ref<chatHistory[]>([])

//查找筛选
filterRole()
//滚动到页面最底部
async function scrollBottom(){
    await nextTick()
    const box=chat_box.value
    if(box){
        box.scrollTop=box.scrollHeight
    }
}
   
//搜索框筛选
async function filterRole(){
    try {
        const res=await axios.get(`http://127.0.0.1/roles?keyword=${input4.value}`)
        console.log('搜索框请求成功,',res.data.data);
        console.log('我是搜索框内容,',input4.value);
        roles.value=res.data.data
        console.log('@@@',roles.value);

    } catch(err) {
        throw err
    }  
}

//开始录音
async function start_record(){
    is_audio.value=false
    if (!recorder) {
            recorder = new Recorder({
                // 以下是默认配置
                sampleBits: 16,
                sampleRate: 16000, // 浏览器默认的输入采样率,
                numChannels: 1,
            });
        }
        recorder.start().then(function () {
            console.log('开始录音');
        });
}

//打开对话弹窗
function openDialog(item:any){
    activeRole.value=item
    dialogTableVisible.value=true
    chat_id.value=Date.now()
    chatHistory.value=[]
}

//添加用户消息
function renderUserMessage(text:string){
    const user_message:chatHistory={
        id:Date.now(),
        role:'user',
        content:text
    }
    chatHistory.value.push(user_message)
}
//更新AI消息
function updateAIMessage(id:number,text:string){
    let msg=chatHistory.value.find(item=>item.id===id)
    if(msg){
        msg.content+=text
        scrollBottom()
    }

}

//ai的回复
let text=''

// 向deepseek的llm发送请求
async function ask_llm(data:any){
    try {
        const result=await fetch('http://127.0.0.1/chat',{
            method:'post',
            body:JSON.stringify(data),
            headers: {
                'Content-Type': 'application/json', 
            },
        })

        let hasStarted = false; // 标记是否开始收到 AI 内容

        const reader = result.body?.getReader()
        const ai_message_id=Date.now()
        //发送的数据块
        let aiReply=''

        while(true){
            const {done,value}= await reader?.read() as ReadableStreamReadResult<Uint8Array>
            if(done){
                console.log('AI回复完成');
                const msg = chatHistory.value.find(m => m.id === ai_message_id)
                if (msg) {
                msg.is_broadcast = true
                }
                break
                
            }
            // value 是 Uint8Array，需要转换为字符串
            const chunk = new TextDecoder().decode(value,{stream:true});
            
            const item_chunk=chunk.split('\n\n')
            for( let word of item_chunk){
                // console.log('@@@',word);
                
                if(word.startsWith('data: ')){
                    aiReply=word.slice(6)
                    text+=aiReply
                    const err_text='{"content":"__APIKEY_ERROR__"}'
                    if (aiReply === err_text) {
                        alert('apikey错误，请联系开发者索要apikey');
                        return
                    } 
                    
                    
                        
                    if(text){
                        if(!hasStarted){
                            hasStarted=true
                            chatHistory.value.push({
                                id:ai_message_id,
                                role:'system',
                                content:'',
                                is_broadcast: false
                            })
                        }
                    }
                    // console.log('接收到的数据块:', aiReply);
                    updateAIMessage(ai_message_id,aiReply)
                }
            }           
        }                 
    } catch(err:any) {
        throw err
    } 
} 

//结束录音
async function end_record(){
    is_audio.value=true
    recorder && recorder.stop();
        console.log('结束录音');

        const audio=recorder.getPCMBlob(); 

        //销毁并置空recorder
        recorder.destroy()
        recorder = null

        const formData=new FormData()
        formData.append('audio',audio)
        let content=''

        
        axios({
            method:'post',
            data:formData,
            url:'http://127.0.0.1/audio'
        }).then(
            async res=>{
                console.log('请求成功',res.data);
                const data=res.data

                if (data.success) {
                    // alert('识别结果：' + data.content);
                    content= res.data.content

                    //添加用户聊天记录
                    renderUserMessage(content) 
                    
                    const chat_json={
                        'role_id':activeRole.value?.id,
                        'message':content,
                        'chat_id':chat_id.value
                    }
                    await ask_llm(chat_json)
                } else {
                    if (data.error === '__APIKEY_ERROR__') {
                        alert('apikey错误，请联系开发者索要apikey');
                    }
                }                              
            },
            err=>{
                console.log('请求失败',err);      
            }
        )
    };

//文字转语音
async function broadcast(text:string,per:any){
    console.log('我是chat.content：',text);
    try {
        const response = await axios.get(`http://127.0.0.1/tts?text=${text}&per=${per}`, {
        responseType: 'arraybuffer'  
        });
            console.log('axios 请求成功');
            console.log('音频数据在：', response.data); //是一个 ArrayBuffer

            //把ArrayBuffer转成Blob URL
            const blob = new Blob([response.data], { type: 'audio/mp3' });
            console.log('@@@',blob);
            
            const audioUrl = URL.createObjectURL(blob);
            console.log('@@@@',audioUrl);

            //播放
            const audio = new Audio(audioUrl);
            await audio.play();

            //释放内存
            audio.onended = () => URL.revokeObjectURL(audioUrl);
    } catch(err:any) {
        throw err
        
    }
}
//文字输入聊天框
async function send_chat(){
    const text=input3.value
    renderUserMessage(text)
    const chat_json={
        'role_id':activeRole.value?.id,
        'message':text,
        'chat_id':chat_id.value
    }
    input3.value=''

    await ask_llm(chat_json)
}

//关闭弹窗
async function closeDialog(){
    try {
        const res=await axios.get(`http://127.0.0.1/delete?chat_id=${chat_id.value}`)
        console.log('删除聊天记录请求成功',res.data);
    } catch(err) {
        throw err
    }
    
}
</script>

<style scoped>
.icon {
    background: linear-gradient(to right,rgb(173, 56, 197) ,rgb(215, 48, 148) );
    width: 50px;
    height: 50px;
    text-align: center;
    line-height: 60px;
    margin-top: 15px;
    border-radius: 10px;
    margin-left: 100px;
    color: #fff;
}
.header {
    height: 90px;
    position: fixed;
    width: 100%;
    top:0;
    left:0;
    display: flex;
    text-align: center;
    justify-content: space-between;
    background-color: #030125;

}
.left {
    display: flex;
}
.title h2 {
    text-align: center;
    margin: 10px 0 0 0;
}
.title div {
    font-size: 12px;
    color: rgb(159, 157, 157);
}
.title {
    color: #fff;
    margin-left: 10px;
}
.right {
    text-align: center;
    line-height: 90px;
    margin-right: 100px;
}
.right span {
    margin-right: 20px;
    color: #fff;
}
.el-button, .el-button.is-round {
    padding: 20px 20px;
    background-color: #8642d3;
    color: #fff;
    border-radius: 10px;
    border: 0px;
}
.main {
    margin-top: 90px ;
    position: absolute;
    top:0;
    left:0;
    right: 0;
    bottom:0;
    background-color: #1b1c31;
}
.search {
    width: 78rem;
    height: 150px;
    background-color: rgba(198, 196, 196, 0.119);
    margin: auto;
    border-radius: 20px;
    padding: 30px;
    color: #fff;
    border:1px solid #8642d3
}
.responsive-input {
    height: 45px;
    width: 90%;
    margin-right: 10px;
}
.search-input {
    height: 45px;
    width: 80%;
    margin-right: 10px;
    margin-left: 30px;
}
.search_btn {
    width: 80px;
    height: 50px;
    background: linear-gradient(to right,#8642d3,#dc184d);
    margin-left: 12px;
    
}
.cancle_btn {
     width: 80px;
    height: 50px;
    background-color: #dc184d;
    margin-left: 12px;
}
.box {
    margin: auto;
    margin-top: 30px;
    display: flex;
    width: 82rem;
    justify-content: space-between;
}
:deep(.el-card__body) {
    padding: 0;
}
.cardfooter {
    /* background-color: aquamarine; */
    margin: 20px;
    color: #fff;
    font-size: 13px;
}
.card {
    width: 280px;
    background-color: #120e24;
    border: 0px;
    border-radius: 10px;
    position: relative;
}
.start_chat {
    width: 120px;
    margin-left: 70px;
    background: linear-gradient(to right,rgb(173, 56, 197) ,rgb(215, 48, 148));
}
.card_font {
    position: absolute;
    color: #fff;
    top:100px;
    left:10px;
    font-size: 13px;
}
.card_font h2 {
    margin: 0;
}
:deep(.dialog) {
    background-color: #1b1c31;
    border-radius: 20px;
    padding: 0;
    border: 1px solid #7632d3;
}
.dialog_header {
    /* background-color: aquamarine; */
    height: 70px;
    display: flex;
    color: #fff;
    font-size: 13px;
}
.dialog_title {
    margin-top: 10px;
}
.dialog_header h2 {
    margin: 0;
}

.dialog_icon {
    height: 60px;
    width: 60px;
    border-radius: 50%;
    margin-left: 20px;
    margin-right: 10px;
}
.dialog_main {
    height: 400px;
    max-height: 400px;
    border-top: 1px solid rgba(128, 128, 128, 0.352);
    border-bottom: 1px solid rgba(128, 128, 128, 0.352);
    display: flex;
    flex-direction: column;
}
.dialog_footer {
    padding-top: 20px;
    padding-bottom: 20px;
}
.chat_message {
    display: flex;
    flex-direction: column;
    margin-top: 15px;
}
.chat_box:last-child {
    margin-bottom: 15px;
}

.chat_message_user {
    background-color: rgb(43, 22, 81);
    width: 500px;
    padding-left: 20px;
    border-radius: 10px;
    align-self: flex-end;
    margin-right: 10px;

}
.chat_message_ai {
    background-color: rgba(67, 63, 74, 0.544);
    padding-left: 20px;
    border-radius: 10px;
    margin-left: 10px;
    width: 500px;
    
}
.chat_nickname {
    color: #bcbbbb;
    font-size: 12px;
    margin-top: 10px;
}
.chat_content {
    color: #fff;
    margin-top: 5px;
}
.broadcast {
    width: 50px;
    border-radius: 10px;
    border: 1px solid black;
    background-color: #7632d3;
    margin-bottom: 5px;
}
</style>